﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using AForge.Imaging.Filters;

namespace QlrrPrintService.Common.Extension
{
    public static class BitmapExtension
    {

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern int InvalidateRect(IntPtr hwnd, IntPtr rect, int bErase);

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern IntPtr GetDC(IntPtr hwnd);

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleDC(IntPtr hdc);

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern int DeleteDC(IntPtr hdc);

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern int BitBlt(IntPtr hdcDst, int xDst, int yDst, int w, int h, IntPtr hdcSrc, int xSrc, int ySrc, int rop);
        static int SRCCOPY = 0x00CC0020;

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        static extern IntPtr CreateDIBSection(IntPtr hdc, ref BITMAPINFO bmi, uint Usage, out IntPtr bits, IntPtr hSection, uint dwOffset);
        static uint BI_RGB = 0;
        static uint DIB_RGB_COLORS = 0;
        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public struct BITMAPINFO
        {
            public uint biSize;
            public int biWidth, biHeight;
            public short biPlanes, biBitCount;
            public uint biCompression, biSizeImage;
            public int biXPelsPerMeter, biYPelsPerMeter;
            public uint biClrUsed, biClrImportant;
            [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 256)]
            public uint[] cols;
        }

        static uint MAKERGB(int r, int g, int b)
        {
            return ((uint)(b & 255)) | ((uint)((r & 255) << 8)) | ((uint)((g & 255) << 16));
        }


        public static Bitmap ConvertTo1bppOr8bpp(this Bitmap b, int bpp)
        {
            if (bpp != 1 && bpp != 8) throw new System.ArgumentException("1 or 8", "bpp");

            // Plan: built into Windows GDI is the ability to convert
            // bitmaps from one format to another. Most of the time, this
            // job is actually done by the graphics hardware accelerator card
            // and so is extremely fast. The rest of the time, the job is done by
            // very fast native code.
            // We will call into this GDI functionality from C#. Our plan:
            // (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed)
            // (2) Create a GDI monochrome hbitmap
            // (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above)
            // (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed)

            int w = b.Width, h = b.Height;
            IntPtr hbm = b.GetHbitmap(); // this is step (1)
            //
            // Step (2): create the monochrome bitmap.
            // "BITMAPINFO" is an interop-struct which we define below.
            // In GDI terms, it's a BITMAPHEADERINFO followed by an array of two RGBQUADs
            BITMAPINFO bmi = new BITMAPINFO();
            bmi.biSize = 40;  // the size of the BITMAPHEADERINFO struct
            bmi.biWidth = w;
            bmi.biHeight = -h;
            bmi.biPlanes = 1; // "planes" are confusing. We always use just 1. Read MSDN for more info.
            bmi.biBitCount = (short)bpp; // ie. 1bpp or 8bpp
            bmi.biCompression = BI_RGB; // ie. the pixels in our RGBQUAD table are stored as RGBs, not palette indexes
            bmi.biSizeImage = (uint)(((w + 7) & 0xFFFFFFF8) * h / 8);
            bmi.biXPelsPerMeter = 1000000; // not really important
            bmi.biYPelsPerMeter = 1000000; // not really important
            // Now for the colour table.
            uint ncols = (uint)1 << bpp; // 2 colours for 1bpp; 256 colours for 8bpp
            bmi.biClrUsed = ncols;
            bmi.biClrImportant = ncols;
            bmi.cols = new uint[256]; // The structure always has fixed size 256, even if we end up using fewer colours
            if (bpp == 1) { bmi.cols[0] = MAKERGB(0, 0, 0); bmi.cols[1] = MAKERGB(255, 255, 255); }
            else { for (int i = 0; i < ncols; i++) bmi.cols[i] = MAKERGB(i, i, i); }
            // For 8bpp we've created an palette with just greyscale colours.
            // You can set up any palette you want here. Here are some possibilities:
            // greyscale: for (int i=0; i<256; i++) bmi.cols[i]=MAKERGB(i,i,i);
            // rainbow: bmi.biClrUsed=216; bmi.biClrImportant=216; int[] colv=new int[6]{0,51,102,153,204,255};
            //          for (int i=0; i<216; i++) bmi.cols[i]=MAKERGB(colv[i/36],colv[(i/6)%6],colv[i%6]);
            // optimal: a difficult topic: http://en.wikipedia.org/wiki/Color_quantization
            // 
            // Now create the indexed bitmap "hbm0"
            IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.
            IntPtr hbm0 = CreateDIBSection(IntPtr.Zero, ref bmi, DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);
            //
            // Step (3): use GDI's BitBlt function to copy from original hbitmap into monocrhome bitmap
            // GDI programming is kind of confusing... nb. The GDI equivalent of "Graphics" is called a "DC".
            IntPtr sdc = GetDC(IntPtr.Zero);       // First we obtain the DC for the screen
            // Next, create a DC for the original hbitmap
            IntPtr hdc = CreateCompatibleDC(sdc); SelectObject(hdc, hbm);
            // and create a DC for the monochrome hbitmap
            IntPtr hdc0 = CreateCompatibleDC(sdc); SelectObject(hdc0, hbm0);
            // Now we can do the BitBlt:
            BitBlt(hdc0, 0, 0, w, h, hdc, 0, 0, SRCCOPY);
            // Step (4): convert this monochrome hbitmap back into a Bitmap:
            System.Drawing.Bitmap b0 = System.Drawing.Bitmap.FromHbitmap(hbm0);
            //
            // Finally some cleanup.
            DeleteDC(hdc);
            DeleteDC(hdc0);
            ReleaseDC(IntPtr.Zero, sdc);
            DeleteObject(hbm);
            DeleteObject(hbm0);
            //
            return b0;
        }

        public static Bitmap ConvertTo24bpp(this Bitmap img)
        {
            var bmp = new Bitmap(img.Width, img.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            using (var gr = Graphics.FromImage(bmp))
                gr.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height));
            return bmp;

        }

        public static Bitmap ConvertTo8bpp(this Bitmap bmp)
        {
            try
            {
                int w = bmp.Width,
          h = bmp.Height,
          r, ic, oc, bmpStride, outputStride, bytesPerPixel;
                PixelFormat pfIn = bmp.PixelFormat;
                ColorPalette palette;
                Bitmap output;
                BitmapData bmpData, outputData;

                //Create the new bitmap
                output = new Bitmap(w, h, PixelFormat.Format8bppIndexed);

                //Build a grayscale color Palette
                palette = output.Palette;
                for (int i = 0; i < 256; i++)
                {
                    Color tmp = Color.FromArgb(255, i, i, i);
                    palette.Entries[i] = Color.FromArgb(255, i, i, i);
                }
                output.Palette = palette;

                //No need to convert formats if already in 8 bit
                if (pfIn == PixelFormat.Format8bppIndexed)
                {
                    output = (Bitmap)bmp.Clone();

                    //Make sure the palette is a grayscale palette and not some other
                    //8-bit indexed palette
                    output.Palette = palette;

                    return output;
                }

                //Get the number of bytes per pixel
                switch (pfIn)
                {
                    case PixelFormat.Format24bppRgb: bytesPerPixel = 3; break;
                    case PixelFormat.Format32bppArgb: bytesPerPixel = 4; break;
                    case PixelFormat.Format32bppRgb: bytesPerPixel = 4; break;
                    default: throw new InvalidOperationException("Image format not supported");
                }

                //Lock the images
                bmpData = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly,
                pfIn);
                outputData = output.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.WriteOnly,
                PixelFormat.Format8bppIndexed);
                bmpStride = bmpData.Stride;
                outputStride = outputData.Stride;

                //Traverse each pixel of the image
                unsafe
                {
                    byte* bmpPtr = (byte*)bmpData.Scan0.ToPointer(),
                    outputPtr = (byte*)outputData.Scan0.ToPointer();

                    if (bytesPerPixel == 3)
                    {
                        //Convert the pixel to it's luminance using the formula:
                        // L = .299*R + .587*G + .114*B
                        //Note that ic is the input column and oc is the output column
                        for (r = 0; r < h; r++)
                            for (ic = oc = 0; oc < w; ic += 3, ++oc)
                                outputPtr[r * outputStride + oc] = (byte)(int)
                                (0.299f * bmpPtr[r * bmpStride + ic] +
                                0.587f * bmpPtr[r * bmpStride + ic + 1] +
                                0.114f * bmpPtr[r * bmpStride + ic + 2]);
                    }
                    else //bytesPerPixel == 4
                    {
                        //Convert the pixel to it's luminance using the formula:
                        // L = alpha * (.299*R + .587*G + .114*B)
                        //Note that ic is the input column and oc is the output column
                        for (r = 0; r < h; r++)
                            for (ic = oc = 0; oc < w; ic += 4, ++oc)
                                outputPtr[r * outputStride + oc] = (byte)(int)
                                ((bmpPtr[r * bmpStride + ic] / 255.0f) *
                                (0.299f * bmpPtr[r * bmpStride + ic + 1] +
                                0.587f * bmpPtr[r * bmpStride + ic + 2] +
                                0.114f * bmpPtr[r * bmpStride + ic + 3]));
                    }
                }

                //Unlock the images
                bmp.UnlockBits(bmpData);
                output.UnlockBits(outputData);

                return output;
            }
            catch
            { }
            return null;

        }

        public static byte[] BmpToByteArray(this Bitmap bmp)
        {
            try
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    //bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
                    bmp.Save(ms, ImageFormat.Bmp);
                    byte[] byteImage = new Byte[ms.Length];
                    ms.Position = 0;
                    ms.Read(byteImage, 0, Convert.ToInt32(ms.Length));
                    ms.Flush();
                    return byteImage;

                }
            }
            catch (Exception ex)
            { }
            return null;

        }

        public static Bitmap Base64StrToBitmap(this string base64str)
        {
            try
            {
                var imgBytes = Convert.FromBase64String(base64str);
                using (var steam = new System.IO.MemoryStream(imgBytes))
                {
                    return Bitmap.FromStream(steam) as Bitmap;
                }
            }
            catch
            { }
            return null;

        }

        public static byte[] Base64StrToByte(this string base64str)
        {
            try
            {
                var imgBytes = Convert.FromBase64String(base64str);
                return imgBytes;
            }
            catch
            { }
            return null;
        }

        public static Bitmap RotateImg(this Bitmap b, int angle)
        {

            IFilter filter = new RotateBilinear(double.Parse(angle.ToString()));
            Bitmap newImage = filter.Apply(b);
            return newImage;

        }

        /// <summary>
        /// 垂直旋转，参见windows画图
        /// </summary>
        /// <param name="mybm"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        public static Bitmap VerticalRoation(this Bitmap mybm, int width, int height)
        {
            Bitmap bm = new Bitmap(width, height);
            int x, y, z;
            Color pixel;

            for (x = 0; x < width; x++)
            {
                for (y = height - 1, z = 0; y >= 0; y--)
                {
                    pixel = mybm.GetPixel(x, y);//获取当前像素的值
                    bm.SetPixel(x, z++, Color.FromArgb(pixel.R, pixel.G, pixel.B));//绘图
                }
            }



            return bm;//返回翻转后的图片
        }

        /// <summary>
        /// 水平旋转，参见windows画图
        /// </summary>
        /// <param name="mybm"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        public static Bitmap HorizontalRoation(this Bitmap mybm, int width, int height)
        {
            Bitmap bm = new Bitmap(width, height);//初始化一个记录经过处理后的图片对象
            int x, y, z;//x,y是循环次数,z是用来记录像素点的x坐标的变化的
            Color pixel;



            for (y = height - 1; y >= 0; y--)
            {
                for (x = width - 1, z = 0; x >= 0; x--)
                {
                    pixel = mybm.GetPixel(x, y);//获取当前像素的值
                    bm.SetPixel(z++, y, Color.FromArgb(pixel.R, pixel.G, pixel.B));//绘图
                }
            }



            return bm;//返回经过翻转后的图片
        }
    }
}
